package com.security.securityproject.config;

import com.security.securityproject.exception.MyExceptionAcessDenidHandler;
import com.security.securityproject.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.util.Assert;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;

/**
 * @author huangkf
 * @version 1.0
 * @date 2022/12/1 : 21:59
 * @description 权限配置类
 */

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //配置错误处理异常类
    @Autowired
    private MyExceptionAcessDenidHandler myExceptionAcessDenidHandler;


    // 注入对象
    @Autowired
    private PersistentTokenRepository  persistentTokenRepository;

    @Autowired
    private UserDetailsServiceImpl  userDetailsService ;

    protected void configure(HttpSecurity http) throws Exception {

        //  remember me

        http.rememberMe().tokenValiditySeconds(120) // 有效时间120 ，单位秒，超过120秒令牌失效,需要重新登录
        .userDetailsService(userDetailsService) // 登录逻辑对象
        .tokenRepository(persistentTokenRepository); // 持久层对象，就是刚配置的自动建表的对象

        //退出登录
        http.logout().logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect("/login");
                    }
                });

        http.formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                // .successForwardUrl("/loginSuccess")   首先页面跳转，会重发请求，但是重定向不会，所以我们当前使用的都是跳转，我们需要改为重定向，不然刷新页面会要求重新登录
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

                        User principal = (User) authentication.getPrincipal();
                        String username = principal.getUsername();
                        System.out.println("获取到的用户名为：" + username);
                        String password = principal.getPassword();  //为了安全考虑，获取到的密码是null的
                        System.out.println("获取到的密码为：" + password);
                        Collection<GrantedAuthority> authorities = principal.getAuthorities();
                        System.out.println("获取到的权限是：" + authorities);

                        //   response.sendRedirect("http://www.baidu.com");    //因为是前后端分离，

                        response.sendRedirect("/loginSuccess");

                    }
                })
                // .failureForwardUrl("/loginfail")
                .failureHandler(new AuthenticationFailureHandler() {  //设置了失败页面的重定向，就需要在权限配置下配置失败页面的权限，不然不会跳转的想应的页面
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.sendRedirect("/loginfail");
                    }
                })
                .usernameParameter("username")
                .passwordParameter("password");
        //    .loginPage("");

        //权限配置  //权限配置
        http.authorizeRequests()
                .antMatchers("/login", "/loginfail")  // ant 期望的，名词蚂蚁， matchers 匹配
                .permitAll()
                //该用户具有某某一个权限才可以访问某个页面
                //.antMatchers("loginSuccess").hasRole("1普通用户1")
                // 3.接口表达式控制权限
                //   .antMatchers("LoginSucess").access("@myAccessServiceImpl.hasPermission(request,authentication)")
                //   .antMatchers("/loginSuccess").hasAuthority("子菜单1")
                // 4、注解的方式，需要全部放行
                .antMatchers("/loginSuccess").permitAll()
                .antMatchers("/adc").hasAuthority("admin2")
                //具有某个角色可以访问该页面
                .antMatchers("/adc").hasRole("superAdmin")
                //具有某个ip可以访问该页面
                .antMatchers("/adc").hasIpAddress("192.168.2.7")
                // .denyAll() //拒绝访问adc的一切请求
                .anyRequest()  //antMatchers 表示授权一个页面，.permitAll()表示不需要授权全部放行，
                .authenticated();    // 除了上面配置的，其他所有的页面的请求(anyRequest)都需要授权(authenticated)才可以访问，。authenticated

/*        public C antMatchers(String... antPatterns) {
            Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
            return chainRequestMatchers(AbstractRequestMatcherRegistry.RequestMatchers.antMatchers(antPatterns));
        }

        antMatchers(String... antPatterns)
        1、可见参数是不定长参数，用于匹配URL规则，如下：
        ?：匹配一个字符
*：匹配0个或多个字符
**：匹配0个或多个目录
     2、 实际项目中，需要放行所有静态资源时，就可以这样写，比如放行js文件夹下所有js脚本

     //js文件夹下所有文件放行
.antMatchers("/js/**").permitAll()
//所有js后缀文件都放行
//  .antMatchers("/**//*.js").permitAll()
.antMatchers("/**//*.js").permitAll()
         */


        //异常配置类
        http.exceptionHandling()
                .accessDeniedHandler(myExceptionAcessDenidHandler); //配置，拒绝访问处理程序为刚才我们编写的没有权限的编写的组件
//另外，同步的话，就是不前后端分离，也可以写一个controller来处理，但现在基本没用
//        //配置异常处理
//        http.exceptionHandling()
//                .accessDeniedPage("/showException");//跳转到指定请求处理


        http.csrf().disable();


    }


}